/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 abstract_combat_object.cpp

	$Header: /heroes4/abstract_combat_object.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "abstract_combat_object.h"

#include "adaptor_handler.h"
#include "battlefield.h"
#include "bound_handler.h"
#include "combat_footprint.h"
#include "combat_object_fader.h"

// -----------------------------------------------------------------------
// combat object base members
// -----------------------------------------------------------------------
t_abstract_combat_object::t_abstract_combat_object( t_battlefield* battlefield )
	: m_view_rect(0,0,0,0)
{
	m_alpha = 15;
	m_maximum_alpha = 15;
	m_shadow_displacement = 0;
	m_list_position = t_combat_object_iterator();
	m_on_draw_list = false;
	if (battlefield != 0) 
	{
		m_battlefield_id = battlefield->get_new_battlefield_id();
	}
	else 
	{
		// This happens when a creature's model is view outside of combat
		// (army window, fort window, etc.)
		m_battlefield_id = 0;
	}
}

t_combat_object_base::~t_combat_object_base()
{
}

t_combat_footprint const& t_combat_object_base::get_footprint() const
{
	return get_combat_footprint( get_footprint_size() );
}

t_map_point_2d t_combat_object_base::get_footprint_center( t_map_point_2d const& cell_point ) const
{
	t_map_point_2d	result = cell_point << k_battlefield_subcell_shift;
	int				half_footprint = get_half_footprint_size();

	result.row += half_footprint;
	result.column += half_footprint;

	return result;
}


bool t_combat_object_base::can_be_attacked() const
{
	return false;
}


bool t_combat_object_base::is_creature() const
{
	return false;
}

bool t_combat_object_base::is_permanent() const
{
	return true;
}

bool t_combat_object_base::in_footprint( t_map_point_2d const& point ) const
{
	int						  footprint_size = get_footprint_size();
	t_combat_footprint const& footprint = get_footprint();
	t_map_point_2d			  offset = point - get_cell_position();

	if (offset.row < 0 || offset.column < 0 || offset.row >= footprint_size
		|| offset.column >= footprint_size)
		return false;
	return footprint[offset.row][offset.column];
}

// -----------------------------------------------------------------------
// combat object base members
// -----------------------------------------------------------------------
t_map_rect_2d t_combat_object_base::get_cell_rect() const
{
	t_map_rect_2d result;
	int			  footprint_size = get_footprint_size();

	result.top_left = get_cell_position();
	result.bottom_right.row = result.top_left.row + footprint_size;
	result.bottom_right.column = result.top_left.column + footprint_size;
	return result;
}

// -----------------------------------------------------------------------
// t_abstract_combat_object members
// -----------------------------------------------------------------------

t_compound_object* t_abstract_combat_object::get_compound_object() const
{
	return 0;
}

t_attackable_object* t_abstract_combat_object::get_attackable_object() 
{
	return 0;
}

// -----------------------------------------------------------------------
// compute drawing depth
// -----------------------------------------------------------------------
int t_abstract_combat_object::get_depth() const
{
	int result;

	// Non-underlays always have a negative depth
	int			   footprint_size         = get_footprint_size();
	int			   footprint_subcell_size = footprint_size
			                                << k_battlefield_subcell_shift;
	t_map_point_2d position               = get_position();
	
	result = -( position.row + position.column + footprint_subcell_size );
	if (is_underlay())
		result += k_depth_underlay;
	return result;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_abstract_combat_object::blocks_movement() const
{
	return true;
}

bool t_abstract_combat_object::is_quicksand() const
{
	return false;
}

// -----------------------------------------------------------------------
// Called from t_battlefield::clear(); absolutely the last chance to clean up
// -----------------------------------------------------------------------
void t_abstract_combat_object::on_battlefield_destruction()
{
	t_attackable_object* attackable = get_attackable_object();

	if ( attackable )
	{
		attackable->on_battlefield_destruction_attackable_object();
	}
}

// -----------------------------------------------------------------------
// set screen positions
// -----------------------------------------------------------------------
void t_abstract_combat_object::set_shadow_displacement( int arg,
													    t_battlefield const& battlefield )
{
	t_map_point_3d shadow_position = m_position;
	
	m_shadow_displacement = arg;
	shadow_position.height -= m_shadow_displacement;
	m_shadow_screen_point = battlefield.subcell_to_screen_point( shadow_position );
}

void t_abstract_combat_object::move( t_battlefield& battlefield, 
									 t_map_point_3d const & new_position )
{
	t_map_point_3d shadow_position = new_position;

	m_position = new_position;
	m_screen_position = battlefield.subcell_to_screen_point( new_position );
	shadow_position.height -= m_shadow_displacement;
	m_shadow_screen_point = battlefield.subcell_to_screen_point( shadow_position );
}

void t_abstract_combat_object::set_screen_position( t_screen_point const& point )
{
	m_shadow_screen_point = point + m_shadow_screen_point - m_screen_position;
	m_screen_position = point;
}

bool t_abstract_combat_object::obscures_vision() const
{
	return false;
}

void t_abstract_combat_object::on_moving()
{
}

void t_abstract_combat_object::on_moved( bool cell_changed )
{
}

void t_abstract_combat_object::on_turned()
{
}

void t_abstract_combat_object::on_turning()
{
}

void t_abstract_combat_object::on_placed()
{
}

void t_abstract_combat_object::on_removed()
{
}

bool t_abstract_combat_object::hinders_movement() const
{
	return false;
}

// -----------------------------------------------------------------------------
// start a creature fading in
// -----------------------------------------------------------------------------
void t_abstract_combat_object::fade_in( t_battlefield& battlefield, int end_alpha, t_combat_action_message const & message )
{
	m_alpha = 0;
	m_maximum_alpha = end_alpha;
	m_fader = new t_combat_object_fader( battlefield, this, true );
	m_fader->display_action_message( message );
}

// -----------------------------------------------------------------------------
// start a creature fading out
// -----------------------------------------------------------------------------
void t_abstract_combat_object::fade_out( t_battlefield& battlefield, t_combat_action_message const & message )
{
	t_handler		    handler;
	t_combat_object_ptr ptr = this;

	m_fader = new t_combat_object_fader( battlefield, this, false );
	handler = add_argument( bound_handler( battlefield, &t_battlefield::remove_object ), ptr );
	m_fader->set_end_handler( handler );
	m_fader->display_action_message( message );
}

// -----------------------------------------------------------------------------
// start a creature fading out
// -----------------------------------------------------------------------------
void t_abstract_combat_object::fade_out( t_battlefield& battlefield, t_handler handler, t_combat_action_message const & message)
{
	m_fader = new t_combat_object_fader( battlefield, this, false );
	m_fader->set_end_handler( handler );
	m_fader->display_action_message( message );
}

// -----------------------------------------------------------------------------
// end a creature fading
// -----------------------------------------------------------------------------
void t_abstract_combat_object::clear_fader()
{
	m_fader = 0;
}

// -----------------------------------------------------------------------
// battlefield class
// set transparency level for a creature
// -----------------------------------------------------------------------
void t_abstract_combat_object::set_alpha( t_battlefield& battlefield, int alpha ) 
{
	assert( alpha >= 0 && alpha <= 15 );
	if (alpha == get_alpha())
		return;
	m_alpha = alpha;
	if (get_list_position()._Mynode() != NULL)
		battlefield.invalidate( *this );
}


int t_abstract_combat_object::get_normal_alpha() const
{
	return 15;
}

bool t_abstract_combat_object::is_elevated() const
{
	return false;
}

// -----------------------------------------------------------------------
// read from saved game - basics
// -----------------------------------------------------------------------
bool t_abstract_combat_object::read_data( std::streambuf& stream )
{
	m_alpha = get<t_uint8>( stream );
	m_battlefield_id = get<t_int32>( stream);
	m_maximum_alpha = get<t_uint8>( stream );
	::read( stream, &m_position, sizeof( m_position ));
	return true;
}

bool t_abstract_combat_object::write_data( std::streambuf& stream ) const
{
	put<t_uint8>( stream, m_alpha );
	put<t_int32>( stream, m_battlefield_id );
	put<t_uint8>( stream, m_maximum_alpha );
	::write( stream, &m_position, sizeof( m_position ));
	return true;
}

void t_abstract_combat_object::place_during_read( t_battlefield& battlefield )
{
	battlefield.place_object( this, get_position() );
}
